package com.xzzz.irda.guardcat.core.metric.memory;

import cn.hutool.core.collection.CollectionUtil;

import java.io.Serializable;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.MemoryUsage;
import java.util.List;

/**
 * 内存信息
 * -XX:+UseParallelGC -XX:+UseParallelOldGC -Xss512K -Xms1408M -Xmx1408M -XX:NewRatio=2 -XX:SurvivorRatio=8 -XX:MetaspaceSize=384M -XX:MaxMetaspaceSize=384M -XX:MaxDirectMemorySize=256M
 *
 * @author xzzz
 * @since 1.0.0
 */
public class MemoryInfo implements Serializable {
    private static final long serialVersionUID = 1L;

    private static final String HEAP_MEMORY = "堆内存";
    private static final String NON_HEAP_MEMORY = "非堆内存";

    private static final String CODE_CACHE = "Code Cache";
    private static final String CODE_CACHE_MEMORY = "JIT 编译代码缓存区内存";

    private static final String METASPACE = "Metaspace";
    private static final String METASPACE_MEMORY = "元空间内存";

    private static final String COMPRESSED_CLASS_SPACE = "Compressed Class Space";
    private static final String COMPRESSED_CLASS_SPACE_MEMORY = "类压缩数据区";

    private static final String EDEN_SPACE = "Eden Space";
    private static final String EDEN_SPACE_MEMORY = "新生代";

    private static final String SURVIVOR_SPACE = "Survivor Space";
    private static final String SURVIVOR_SPACE_MEMORY = "幸存代";

    private static final String OLD_GEN = "Old Gen";
    private static final String OLD_GEN_MEMORY = "老年代";


    /**
     * 堆内存使用信息
     * <p>
     * Java 虚拟机有一个 heap，它是运行时数据区域，所有类实例和数组的内存都从该区域分配。
     * 1. 它是在 Java 虚拟机启动时创建的。
     * 2. 对象的堆内存由称为 垃圾收集器 的自动内存管理系统回收。
     * 3. 堆可以是固定大小的，也可以是扩展和收缩的。
     * 4. 堆的内存不需要是连续的。
     */
    private BaseMemoryUsageInfo heapUsage;

    /**
     * 非堆内存使用信息
     * <p>
     * Java 虚拟机管理堆以外的内存（称为：非堆内存）。
     * <p>
     * 1、Java 虚拟机有一个在所有线程之间共享的方法区。
     * 方法区属于非堆内存。
     * 它存储每个类的结构，例如：运行时常量池、字段和方法数据，以及方法和构造函数的代码。
     * 它是在 Java 虚拟机启动时创建的。
     * 方法区在逻辑上是堆的一部分，但 Java 虚拟机实现可以选择不进行垃圾收集或压缩它。
     * 与堆类似，方法区可以是固定大小的，也可以是扩展和收缩的。方法区的内存不需要是连续的。
     * <p>
     * 2、除了方法区，Java 虚拟机实现可能需要内存用于内部处理或优化，这也属于非堆内存。
     * 例如：JIT 编译器需要内存来存储从 Java 虚拟机代码转换而来的本地机器代码，以实现高性能。
     */
    private BaseMemoryUsageInfo nonHeapUsage;

    /**
     * JIT 代码缓存区内存使用信息
     */
    private MemoryUsageInfo codeCacheUsage;

    /**
     * 元空间内存使用信息
     */
    private MemoryUsageInfo metaspaceUsage;

    /**
     * 类指针压缩空间内存使用信息
     */
    private MemoryUsageInfo compressedClassSpace;

    /**
     * 新生代内存使用信息
     */
    private MemoryUsageInfo edenSpace;

    /**
     * 幸存代内存使用信息
     */
    private MemoryUsageInfo survivorSpace;

    /**
     * 老年代内存使用信息
     */
    private MemoryUsageInfo oldGen;

    /**
     * 构造器私有化
     */
    private MemoryInfo() {
    }

    /**
     * 获取堆内存使用信息
     *
     * @return BaseMemoryUsageInfo 基础内存使用信息
     */
    public static BaseMemoryUsageInfo getHeapMemoryUsageInfo() {
        return getHeapMemoryUsageInfo(null);
    }

    /**
     * 获取堆内存使用信息
     *
     * @param memoryMxBean Java 虚拟机内存系统的管理接口
     * @return BaseMemoryUsageInfo 基础内存使用信息
     */
    private static BaseMemoryUsageInfo getHeapMemoryUsageInfo(MemoryMXBean memoryMxBean) {
        if (null == memoryMxBean) {
            memoryMxBean = ManagementFactory.getMemoryMXBean();
        }

        MemoryUsage heapMemoryUsage = memoryMxBean.getHeapMemoryUsage();
        UsageInfo heapUsageInfo = new UsageInfo(heapMemoryUsage.getInit(), heapMemoryUsage.getMax(), heapMemoryUsage.getCommitted(), heapMemoryUsage.getUsed());
        return new BaseMemoryUsageInfo(HEAP_MEMORY, heapUsageInfo);
    }

    /**
     * 获取非堆内存使用信息
     *
     * @return BaseMemoryUsageInfo 基础内存使用信息
     */
    public static BaseMemoryUsageInfo getNonHeapMemoryUsageInfo() {
        return getNonHeapMemoryUsageInfo(null);
    }

    /**
     * 获取非堆内存使用信息
     *
     * @param memoryMxBean Java 虚拟机内存系统的管理接口
     * @return BaseMemoryUsageInfo 基础内存使用信息
     */
    private static BaseMemoryUsageInfo getNonHeapMemoryUsageInfo(MemoryMXBean memoryMxBean) {
        if (null == memoryMxBean) {
            memoryMxBean = ManagementFactory.getMemoryMXBean();
        }

        MemoryUsage nonHeapMemoryUsage = memoryMxBean.getNonHeapMemoryUsage();

        UsageInfo nonHeapUsageInfo = new UsageInfo(nonHeapMemoryUsage.getInit(), nonHeapMemoryUsage.getMax(), nonHeapMemoryUsage.getCommitted(), nonHeapMemoryUsage.getUsed());
        return new BaseMemoryUsageInfo(NON_HEAP_MEMORY, nonHeapUsageInfo);
    }

    /**
     * 获取 JIT 编译代码缓存区内存内存使用信息
     *
     * @return MemoryUsageInfo  内存使用信息
     */
    public static MemoryUsageInfo getCodeCacheMemoryUsageInfo() {
        UsageInfo[] usageInfoArr = getUsageInfo(CODE_CACHE);
        return getCodeCacheMemoryUsageInfo(usageInfoArr[0], usageInfoArr[1], usageInfoArr[2]);
    }

    /**
     * 获取 JIT 编译代码缓存区内存内存使用信息
     *
     * @param usageInfo           内存使用情况
     * @param peakUsageInfo       内存使用峰值情况
     * @param collectionUsageInfo GC 之后的内存使用情况
     * @return MemoryUsageInfo
     */
    private static MemoryUsageInfo getCodeCacheMemoryUsageInfo(UsageInfo usageInfo, UsageInfo peakUsageInfo, UsageInfo collectionUsageInfo) {
        return new MemoryUsageInfo(CODE_CACHE_MEMORY, usageInfo, NON_HEAP_MEMORY, peakUsageInfo, collectionUsageInfo);
    }

    /**
     * 获取元空间内存使用信息
     *
     * @return MemoryUsageInfo  内存使用信息
     */
    public static MemoryUsageInfo getMetaspaceMemoryUsageInfo() {
        UsageInfo[] usageInfoArr = getUsageInfo(METASPACE);
        return getMetaspaceMemoryUsageInfo(usageInfoArr[0], usageInfoArr[1], usageInfoArr[2]);
    }

    /**
     * 获取元空间内存使用信息
     *
     * @param usageInfo           内存使用情况
     * @param peakUsageInfo       内存使用峰值情况
     * @param collectionUsageInfo GC 之后的内存使用情况
     * @return MemoryUsageInfo
     */
    private static MemoryUsageInfo getMetaspaceMemoryUsageInfo(UsageInfo usageInfo, UsageInfo peakUsageInfo, UsageInfo collectionUsageInfo) {
        return new MemoryUsageInfo(METASPACE_MEMORY, usageInfo, NON_HEAP_MEMORY, peakUsageInfo, collectionUsageInfo);
    }

    /**
     * 获取类压缩数据区内存使用信息
     *
     * @return MemoryUsageInfo  内存使用信息
     */
    public static MemoryUsageInfo getCompressedClassSpaceMemoryUsageInfo() {
        UsageInfo[] usageInfoArr = getUsageInfo(COMPRESSED_CLASS_SPACE);
        return getCompressedClassSpaceMemoryUsageInfo(usageInfoArr[0], usageInfoArr[1], usageInfoArr[2]);
    }

    /**
     * 获取类压缩数据区内存使用信息
     *
     * @param usageInfo           内存使用情况
     * @param peakUsageInfo       内存使用峰值情况
     * @param collectionUsageInfo GC 之后的内存使用情况
     * @return MemoryUsageInfo
     */
    private static MemoryUsageInfo getCompressedClassSpaceMemoryUsageInfo(UsageInfo usageInfo, UsageInfo peakUsageInfo, UsageInfo collectionUsageInfo) {
        return new MemoryUsageInfo(COMPRESSED_CLASS_SPACE_MEMORY, usageInfo, NON_HEAP_MEMORY, peakUsageInfo, collectionUsageInfo);
    }

    /**
     * 获取新生代内存使用信息
     *
     * @return MemoryUsageInfo  内存使用信息
     */
    public static MemoryUsageInfo getEdenSpaceMemoryUsageInfo() {
        UsageInfo[] usageInfoArr = getUsageInfo(EDEN_SPACE);
        return getEdenSpaceMemoryUsageInfo(usageInfoArr[0], usageInfoArr[1], usageInfoArr[2]);
    }

    /**
     * 获取新生代内存使用信息
     *
     * @param usageInfo           内存使用情况
     * @param peakUsageInfo       内存使用峰值情况
     * @param collectionUsageInfo GC 之后的内存使用情况
     * @return MemoryUsageInfo
     */
    private static MemoryUsageInfo getEdenSpaceMemoryUsageInfo(UsageInfo usageInfo, UsageInfo peakUsageInfo, UsageInfo collectionUsageInfo) {
        return new MemoryUsageInfo(EDEN_SPACE_MEMORY, usageInfo, HEAP_MEMORY, peakUsageInfo, collectionUsageInfo);
    }

    /**
     * 获取幸存代内存使用信息
     *
     * @return MemoryUsageInfo  内存使用信息
     */
    public static MemoryUsageInfo getSurvivorSpaceMemoryUsageInfo() {
        UsageInfo[] usageInfoArr = getUsageInfo(SURVIVOR_SPACE);
        return getSurvivorSpaceMemoryUsageInfo(usageInfoArr[0], usageInfoArr[1], usageInfoArr[2]);
    }

    /**
     * 获取幸存代内存使用信息
     *
     * @param usageInfo           内存使用情况
     * @param peakUsageInfo       内存使用峰值情况
     * @param collectionUsageInfo GC 之后的内存使用情况
     * @return MemoryUsageInfo
     */
    private static MemoryUsageInfo getSurvivorSpaceMemoryUsageInfo(UsageInfo usageInfo, UsageInfo peakUsageInfo, UsageInfo collectionUsageInfo) {
        return new MemoryUsageInfo(SURVIVOR_SPACE_MEMORY, usageInfo, HEAP_MEMORY, peakUsageInfo, collectionUsageInfo);
    }

    /**
     * 获取老年代内存使用信息
     *
     * @return MemoryUsageInfo  内存使用信息
     */
    public static MemoryUsageInfo getOldGenMemoryUsageInfo() {
        UsageInfo[] usageInfoArr = getUsageInfo(OLD_GEN);
        return getOldGenMemoryUsageInfo(usageInfoArr[0], usageInfoArr[1], usageInfoArr[2]);
    }

    /**
     * 获取老年代内存使用信息
     *
     * @param usageInfo           内存使用情况
     * @param peakUsageInfo       内存使用峰值情况
     * @param collectionUsageInfo GC 之后的内存使用情况
     * @return MemoryUsageInfo
     */
    private static MemoryUsageInfo getOldGenMemoryUsageInfo(UsageInfo usageInfo, UsageInfo peakUsageInfo, UsageInfo collectionUsageInfo) {
        return new MemoryUsageInfo(OLD_GEN_MEMORY, usageInfo, HEAP_MEMORY, peakUsageInfo, collectionUsageInfo);
    }

    /**
     * 获取使用信息
     *
     * @param keyWord 名称关键字
     * @return UsageInfo[] 使用信息数组
     */
    private static UsageInfo[] getUsageInfo(String keyWord) {
        List<MemoryPoolMXBean> memoryPoolMxBeanList = ManagementFactory.getMemoryPoolMXBeans();
        UsageInfo[] usageInfoArr = new UsageInfo[3];
        if (CollectionUtil.isNotEmpty(memoryPoolMxBeanList)) {
            MemoryUsage usage;
            MemoryUsage peakUsage;
            MemoryUsage collectionUsage;
            String name;
            for (MemoryPoolMXBean memoryPoolMxBean : memoryPoolMxBeanList) {
                name = memoryPoolMxBean.getName();
                if (null != name && name.contains(keyWord)) {
                    usage = memoryPoolMxBean.getUsage();
                    peakUsage = memoryPoolMxBean.getPeakUsage();
                    collectionUsage = memoryPoolMxBean.getCollectionUsage();

                    usageInfoArr[0] = new UsageInfo(usage.getInit(), usage.getMax(), usage.getCommitted(), usage.getUsed());
                    usageInfoArr[1] = new UsageInfo(peakUsage.getInit(), peakUsage.getMax(), peakUsage.getCommitted(), peakUsage.getUsed());
                    if (collectionUsage != null) {
                        usageInfoArr[2] = new UsageInfo(collectionUsage.getInit(), collectionUsage.getMax(), collectionUsage.getCommitted(), collectionUsage.getUsed());
                    }
                    break;
                }
            }
        }
        return usageInfoArr;
    }

    /**
     * 获取内存信息
     *
     * @return MemoryInfo 内存信息
     */
    public static MemoryInfo getMemoryInfo() {
        MemoryInfo memoryInfo = new MemoryInfo();

        MemoryMXBean memoryMxBean = ManagementFactory.getMemoryMXBean();

        memoryInfo.heapUsage = getHeapMemoryUsageInfo(memoryMxBean);
        memoryInfo.nonHeapUsage = getNonHeapMemoryUsageInfo(memoryMxBean);

        List<MemoryPoolMXBean> memoryPoolMxBeanList = ManagementFactory.getMemoryPoolMXBeans();
        if (CollectionUtil.isNotEmpty(memoryPoolMxBeanList)) {
            UsageInfo usageInfo;
            UsageInfo peakUsageInfo;
            UsageInfo collectionUsageInfo = null;

            MemoryUsage usage;
            MemoryUsage peakUsage;
            MemoryUsage collectionUsage;
            String name;
            for (MemoryPoolMXBean memoryPoolMxBean : memoryPoolMxBeanList) {
                name = memoryPoolMxBean.getName();
                usage = memoryPoolMxBean.getUsage();
                peakUsage = memoryPoolMxBean.getPeakUsage();
                collectionUsage = memoryPoolMxBean.getCollectionUsage();

                usageInfo = new UsageInfo(usage.getInit(), usage.getMax(), usage.getCommitted(), usage.getUsed());
                peakUsageInfo = new UsageInfo(peakUsage.getInit(), peakUsage.getMax(), peakUsage.getCommitted(), peakUsage.getUsed());
                collectionUsageInfo = null;
                if (collectionUsage != null) {
                    collectionUsageInfo = new UsageInfo(collectionUsage.getInit(), collectionUsage.getMax(), collectionUsage.getCommitted(), collectionUsage.getUsed());
                }
                if (null == name) {
                    continue;
                }
                if (name.contains("Code Cache")) {
                    memoryInfo.codeCacheUsage = getCodeCacheMemoryUsageInfo(usageInfo, peakUsageInfo, collectionUsageInfo);
                } else if (name.contains(METASPACE)) {
                    memoryInfo.metaspaceUsage = getMetaspaceMemoryUsageInfo(usageInfo, peakUsageInfo, collectionUsageInfo);
                } else if (name.contains(COMPRESSED_CLASS_SPACE)) {
                    memoryInfo.compressedClassSpace = getCompressedClassSpaceMemoryUsageInfo(usageInfo, peakUsageInfo, collectionUsageInfo);
                } else if (name.contains(EDEN_SPACE)) {
                    memoryInfo.edenSpace = getEdenSpaceMemoryUsageInfo(usageInfo, peakUsageInfo, collectionUsageInfo);
                } else if (name.contains(SURVIVOR_SPACE)) {
                    memoryInfo.survivorSpace = getSurvivorSpaceMemoryUsageInfo(usageInfo, peakUsageInfo, collectionUsageInfo);
                } else if (name.contains(OLD_GEN)) {
                    memoryInfo.oldGen = getOldGenMemoryUsageInfo(usageInfo, peakUsageInfo, collectionUsageInfo);
                }
            }
        }

        return memoryInfo;
    }

    @Override
    public String toString() {
        return "内存信息{" +
                "堆内存信息=" + heapUsage +
                ", 非堆内存信息=" + nonHeapUsage +
                ", JIT代码缓存区内存信息=" + codeCacheUsage +
                ", 元空间内存信息=" + metaspaceUsage +
                ", 类压缩数据区内存信息=" + compressedClassSpace +
                ", 新生代=" + edenSpace +
                ", 幸存代=" + survivorSpace +
                ", 老年代=" + oldGen +
                '}';
    }

    public BaseMemoryUsageInfo getHeapUsage() {
        return heapUsage;
    }

    public BaseMemoryUsageInfo getNonHeapUsage() {
        return nonHeapUsage;
    }

    public MemoryUsageInfo getCodeCacheUsage() {
        return codeCacheUsage;
    }

    public MemoryUsageInfo getMetaspaceUsage() {
        return metaspaceUsage;
    }

    public MemoryUsageInfo getCompressedClassSpace() {
        return compressedClassSpace;
    }

    public MemoryUsageInfo getEdenSpace() {
        return edenSpace;
    }

    public MemoryUsageInfo getSurvivorSpace() {
        return survivorSpace;
    }

    public MemoryUsageInfo getOldGen() {
        return oldGen;
    }
}
